/**
 * 19.11.28 增加resetAll()中的排除项
 * 19.12.9 规范化
*/

import {Utils} from "./utils";

/**
 * 属性类
 * @param key {string} 属性名
 * @param value {*} 原始值
 */
class Prop{
    constructor(key,value){
        this.type=Utils.typeOf(value);
        this.key=key;
        this.value=value;
        this.oldValue=value;
        this.oriValue=value;
        this.alwaysCallback=false;//是否总是执行回调
    }

    /**
     * 销毁
     * @returns {Function}
     */
    get dispose(){
        return ()=>{
            for(let key in this){
                this[''+key+'']=null;
            }
        };
    }

    /**
     * 获取上一次值
     * @returns {*}
     */
    get old(){
        return this.oldValue;
    }

    /**
     * 获取当前值
     * @returns {*}
     */
    get value(){
        return this._value;
    }

    /**
     * 设置当前值
     * @param val
     */
    set value(val){
        if(!this.alwaysCallback) {
            if (Utils.equal(this.oldValue, val)){
                return;
            }
        }
        let n=val;
        let o=this.oldValue;
        this._value=val;
        this.oldValue=val;
        this.callback(n,o);
    }

    /**
     * 重置回原始值
     * @param call {Boolean} 是否执行值改变的回调
     */
    reset(call=false){
        let n=this.oriValue;
        let o=this._value;
        this._value = this.oriValue;
        this.oldValue=this.oriValue;
        if(!call)return;
        this.callback(n,o);
    }

    /**
     * 设置值改变时的回调
     * @param callback
     * @returns {Prop}
     */
    onChange(callback){
        this.callback=callback;
        return this;
    }

    /**
     * 值改变时的回调
     */
    callback(){}
}

/**
 * 属性（属性赋值内部没有做引用相关的处理，自行在外部处理）
 * @param props {object} 键为属性名，值为该属性原始值
 * example: let _props=new props({a:0,b:true,c:null});
 */
export default class Props{
    constructor(props={}){
        this._type='Props';
        this._finalKey=null;//执行键
        this._init(props);
    }

    //---------------------------------------对外属性及方法--------------------------------------------

    /**
     * 获取类型
     * @returns {string}
     */
    get type(){
        return this._type;
    }

    /**
     * 设置执行键（当同时改变多个会引起相同操作的属性时，只在执行键改变时才执行操作）
     * 当多个属性需要同时频繁的更改时，执行键可大幅提升性能
     * @param key {string} 属性名
     */
    setFinalKey(key){
        this._finalKey=key;
    }

    /**
     * 重置执行键（将执行键置空，否则只在执行键改变时才执行回调函数）
     */
    resetFinalKey(){
        this._finalKey=null;
    }

    /**
     * 添加属性
     * @param props {object} 键为属性名，值为该属性原始值
     * example: _props.add({d:0,e:false});
     */
    add(props={}){
        this._setKeys(props);
    }

    /**
     * 移除属性
     * @param keys {...string} 属性名
     */
    remove(...keys){
        for(let key of keys) {
            this.callbacks[key] = null;
            this[key].dispose();
            this[key] = null;
        }
    }

    /**
     * 获取所有属性
     * @param onlyValue {boolean} 是否只获取值
     * @return {object}
     */
    all(onlyValue=false){
        let out={};
        for(let key of Object.keys(this)){
            if(this[key] instanceof Prop){
                if(onlyValue){
                    out[key]=this[key].value;
                }else {
                    out[key] = this[key];
                }
            }
        }
        return out;
    }

    /**
     * 销毁
     */
    dispose(){
        for(let key of Object.keys(this)){
            if(this[key] instanceof Prop){
                this[key].dispose();
            }
        }
    }

    /**
     * 重设所有元素到原始值（是否执行回调，排除在外的键）
     * @param call {Boolean} 是否执行回调
     * @param exceptions {string} 排除在外的属性名
     */
    resetAll(call=false,...exceptions){
        for(let key of Object.keys(this)){
            if(exceptions.indexOf(key)!==-1)continue;
            if(this[key] instanceof Prop){
                this[key].reset(call);
            }
        }
    }

    /**
     * 某一属性改变时执行回调
     * @param key {String} 属性名
     * @param callback {Function} 回调函数
     * @returns {props}
     */
    onKeyChange(key,callback){
        if(this.callbacks[key])this.callbacks[key]=callback;
        return this;
    }

    /**
     * 任一属性改变时执行回调
     * @param callback
     * @returns {props}
     */
    onChange(callback){
        this._onChangeCallback=callback;
        return this;
    }

    /**
     * 多个属性改变时执行同一回调
     * @param callback
     * @param keys
     */
    onMultiKeyChange(callback,...keys){
        keys.forEach((key)=>{
            if(this.callbacks[key])this.callbacks[key]=callback;
        });
    }

    //----------------------------------------私有属性及方法------------------------------------------

    _init(props){
        this.callbacks={};
        this._setKeys(props);
    }
    _setKeys(props){
        let keys=Object.keys(props);
        for(let i=0;i<keys.length;i++){
            let key=keys[i];
            this.callbacks[key]=()=>{};
            this[key]=new Prop(key,props[key]);
            this[key].onChange((n,o)=>{
                if(this._finalKey!==key&&this._finalKey!==undefined&&this._finalKey!==null)return;
                this._onChangeCallback();
                this.callbacks[key](n,o);
            });
        }
    }
    _onChangeCallback(){}
}